/* A simple synchronous XML-RPC client written in C, as an example of
 an Xmlrpc-c client.  This invokes the sample.add procedure that the
 Xmlrpc-c example server.c server provides.  I.e. it adds to numbers
 together, the hard way.
 */
//#define XMLRPC_WANT_INTERNAL_DECLARATIONS
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <xmlrpc-c/base.h>
#include <xmlrpc-c/client.h>

#define ALL 1
#define ANY 2
#define MAJORITY 3

//#include "config.h"  /* information about this build environment */

#define NAME "Xmlrpc-c Test Client"
#define VERSION "1.0"

struct timeval *start_time;
long int *request_time;
int numoftail = 0;
int numofhead = 0;
int numoferror = 0;
int returned = 0;
struct timeval time1, time2, sta;

static void die_if_fault_occurred(xmlrpc_env * const envP) {
	if (envP->fault_occurred) {
		fprintf(stderr, "XML-RPC Fault: %s (%d)\n", envP->fault_string,
				envP->fault_code);
		exit(1);
	}
}

long int time_diff(struct timeval time1, struct timeval time2) {
	long int ans = (time2.tv_sec - time1.tv_sec) * 1000000
			+ (time2.tv_usec - time1.tv_usec);
	return ans;
}

static void sync_call(xmlrpc_env * env, const char ** serverUrls,
		int numofserver, int numoftoss, int semantic, const char * methodName) {
	int i;
	int numoftail = 0;
	int numofhead = 0;
	int numoferror = 0;
	xmlrpc_value *resultP;
	for (i = 0; i < numoftoss; i++) {
		/* Make the remote procedure call */
		resultP = xmlrpc_client_call(env, serverUrls, numofserver, semantic,
				methodName, "(s)", "toss");
		die_if_fault_occurred(env);

		const char * const stringValueP;
		/* Get toss result and print it out. */
		xmlrpc_read_string(env, resultP, &stringValueP);
		//printf("Toss result is %s\n", stringValueP);
		if (strcmp("Head", stringValueP) == 0)
			numofhead++;
		if (strcmp("Tail", stringValueP) == 0)
			numoftail++;
		if (strcmp("Error", stringValueP) == 0)
			numoferror++;
	}
	//xmlrpc_DECREF(resultP);
	printf("%d|%d|%d|%d|%d\n", semantic, numofhead, numoftail, numoferror, 1);
}

xmlrpc_value *
xmlrpc_string_new(xmlrpc_env * const envP, const char * const value);

xmlrpc_value *
xmlrpc_build_value(xmlrpc_env * const envP, const char * const format, ...);

static void async_call(xmlrpc_env * env, const char ** serverUrls,
		int numServer, int numoftoss, int semantic, const char * methodName);

int main(int const argc, const char ** const argv) {
	xmlrpc_env env;
	xmlrpc_value * resultP = NULL;
	xmlrpc_int32 sum;

	/*------*/
	/*
	 xmlrpc_env_init(&env);
	 const char* nr = "new";

	 const char* string = "hello world";
	 resultP = xmlrpc_string_new(&env, string);//xmlrpc_build_value(&env, "s", string);
	 //xmlrpc_string_new(&env, string);
	 printf("rv:%d \n", resultP);
	 //printf("rv:%d \n", resultP->_type);
	 xmlrpc_read_string(&env, resultP, &nr);
	 printf("read string : %s \n", nr);
	 */

	//int semantic = 2;
	int numofserver = 3;
	int numoftoss = 10;
	if (argc - 1 >= 1)
		numoftoss = atoi(argv[1]);
	if (argc - 1 >= 2) {
		numofserver = atoi(argv[2]);
		if (numofserver % 2 == 0) {
			fprintf(stderr, "Must be odd number\n");
			exit(1);
		}
		if (numofserver < 3 || numofserver > 11) {
			fprintf(stderr, "between 3 and 11\n");
			exit(1);
		}

	}
	char **serverUrls;
	serverUrls = (char**) malloc(sizeof(char*) * numofserver);
	int i;
	for (i = 0; i < numofserver; i++) {
		serverUrls[i] = (char*) malloc(sizeof(char) * 256);
		sprintf(serverUrls[i], "http://localhost:80%d/RPC2", i + 10);
	}
	const char * const methodName = "toss";

	/* Initialize our error-handling environment. */
	xmlrpc_env_init(&env);

	/* Start up our XML-RPC client library. */
	xmlrpc_client_init2(&env, XMLRPC_CLIENT_NO_FLAGS, NAME, VERSION, NULL, 0);
	die_if_fault_occurred(&env);

	int semantic;
	for (semantic = 1; semantic <= 3; semantic++) {

		async_call(&env, serverUrls, numofserver, numoftoss, semantic,
				methodName);

		wait_multiloop_finish();
		double avgtime = 0;
		for (i = 0; i < numoftoss; i++) {
			avgtime = avgtime + request_time[i];
		}
		printf("%d|%d|%d|%d|%d|%f\n", semantic, numofhead, numoftail,
				numoferror, 2, avgtime / numoftoss / 1000);
	}
	/* Clean up our error-handling environment. */
	xmlrpc_env_clean(&env);

	/* Shutdown our XML-RPC client library. */
	xmlrpc_client_cleanup();
}

static void handle_response(const char * const serverUrl,
		const char * const methodName, xmlrpc_value * const paramArrayP,
		void * const user_data, xmlrpc_env * const faultP,
		xmlrpc_value * const resultP) {

	xmlrpc_env env;

	//xmlrpc_int addend, adder;

	/* Initialize our error environment variable */
	xmlrpc_env_init(&env);

	if (faultP->fault_occurred)
		printf("The RPC failed.  %s\n", faultP->fault_string);
	else {

		char* stringValueP;

		xmlrpc_read_string(&env, resultP, &stringValueP);
		die_if_fault_occurred(&env);

		//printf("received : %s \n", stringValueP);
	}
//
//	printf("RPC with method '%s' at URL '%s' to add %d and %d "
//			"has completed\n", methodName, serverUrl, addend, adder);
//

//		xmlrpc_int sum;
//
//		xmlrpc_read_int(&env, resultP, &sum);
//		die_if_fault_occurred(&env);
//
//		printf("The sum is  %d\n", sum);
//	}

}

static void real_response(const char * const serverUrl,
		const char * const methodName, xmlrpc_value * const paramArrayP,
		void * const user_data, xmlrpc_env * const faultP,
		xmlrpc_value * const resultP) {
	xmlrpc_env env;

	//xmlrpc_int addend, adder;

	/* Initialize our error environment variable */
	xmlrpc_env_init(&env);

	if (faultP->fault_occurred) {
		//printf("The RPC failed.  %s\n", faultP->fault_string);
		numoferror++;
	} else {
		const char * const stringValueP;
		struct timeval current;
		gettimeofday(&current, NULL);

		request_time[returned] = time_diff(start_time[returned], current);
		returned++;

		xmlrpc_read_string(&env, resultP, &stringValueP);
		//printf("end %d: %s \n", returned, stringValueP);
		if (strcmp("Head", stringValueP) == 0)
			numofhead++;
		if (strcmp("Tail", stringValueP) == 0)
			numoftail++;
		if (strcmp("Error", stringValueP) == 0)
			numoferror++;
	}
	//xmlrpc_DECREF(resultP);

}

void testAsynch(char **serverUrls, int numServer, int numToss, char* methodName) {

	printf("method: %s\n", methodName);
	multicall_asynch(serverUrls, numServer, methodName, real_response, MAJORITY,
			NULL, "(s)", "toss");

//	multicall_asynch(serverUrls, numServer, methodName, handle_response, ALL,
//			NULL, "(s)", "toss");

//	multicall_asynch(serverUrls, numServer, methodName, handle_response,
//			MAJORITY, NULL, "(s)", "toss");
}

static void async_call(xmlrpc_env * env, const char ** serverUrls,
		int numServer, int numoftoss, int semantic, const char * methodName) {

	start_time = malloc(sizeof(struct timeval) * numoftoss);
	request_time = malloc(sizeof(long int) * numoftoss);
	int i;

	//xmlrpc_value *resultP;
	numofhead = 0;
	numoftail = 0;
	numoferror = 0;
	returned = 0;
	for (i = 0; i < numoftoss; i++) {
		request_time[i] = 0;
		gettimeofday(&start_time[i], NULL);
		/* Make the remote procedure call */
		multicall_asynch(serverUrls, numServer, methodName, real_response,
				semantic, NULL, "(s)", "toss");
		die_if_fault_occurred(env);
		if (i % 10 == 0)
		{
			//printf("10 waits...\n");
			wait_multiloop_finish();
		}
	}
}

void testSynch(xmlrpc_env env, char **serverUrls, int numServer, int numToss,
		char* methodName) {

	sync_call(&env, serverUrls, numServer, numToss, 1, methodName);
	sync_call(&env, serverUrls, numServer, numToss, 2, methodName);
	sync_call(&env, serverUrls, numServer, numToss, 3, methodName);
	//xmlrpc_value *resultPs;
	//resultPs = xmlrpc_array_new(&env);
	/* Make the remote procedure call */
	//resultP = xmlrpc_client_call(&env, serverUrls, numofserver, semantic, methodName, "(s)", "toss");
	//die_if_fault_occurred(&env);
	//const char * const stringValueP;
	/* Get toss result and print it out. */
	//xmlrpc_read_string(&env, resultP, &stringValueP);
	//printf("Toss result is %s\n", stringValueP);
	/*int size = xmlrpc_array_size(&env, resultPs);
	 xmlrpc_value * firstElementP;
	 for(i=0;i<size;i++)
	 {
	 xmlrpc_array_read_item(&env, resultPs, i, &firstElementP);
	 xmlrpc_read_string(&env, firstElementP, &stringValueP);
	 die_if_fault_occurred(&env);
	 printf("Toss result is %s\n", stringValueP);
	 }*/
	/* Dispose of our result value. */
	//xmlrpc_DECREF(resultP);
	return;
}

